#!/usr/bin/env python3
"""
会议录音转PRD工具 - 重构版（增强鲁棒性）
基于FunASR + 本地LLM的3阶段流程
Stage1: 纯语音转录 + 说话人分离
Stage2: 智能语义分析 (角色识别 + 需求提取)  
Stage3: 报告生成 (PRD + 会议纪要)

增强功能：
- 自动检测大文件并分割处理
- 依赖检查（ffmpeg/ffprobe）
- 内存优化和垃圾回收
- 增强错误处理和日志记录
"""

import os
import sys
import argparse
import time
from pathlib import Path
import logging
import subprocess
import gc
import warnings
from datetime import timedelta
from typing import Tuple, List, Dict

# 添加项目根目录到路径
sys.path.insert(0, str(Path(__file__).parent.parent))

# 配置日志和警告
warnings.filterwarnings("ignore")
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

# 设置环境变量防止段错误
os.environ['OMP_NUM_THREADS'] = '1'
os.environ['MKL_NUM_THREADS'] = '1'
os.environ['OPENBLAS_NUM_THREADS'] = '1'

# 导入对话服务配置
try:
    sys.path.insert(0, str(Path(__file__).parent.parent / "dialogue_service"))
    from app.core.config import settings, file_config
except ImportError:
    # 如果无法导入配置，创建默认配置类
    from dataclasses import dataclass
    
    @dataclass
    class DefaultFileSizeConfig:
        """默认文件处理配置"""
        large_file_threshold: int = 300  # s
        chunk_size: int = 300  # s
        
    file_config = DefaultFileSizeConfig()
    logger.warning("无法导入配置文件，使用默认配置")

from dialogue_process.stages.stage1_raw import Stage1Processor
from dialogue_process.stages.stage2_ai_analysis import Stage2SemanticAnalyzer
from dialogue_process.stages.stage3_report import Stage3ReportGenerator

class MeetingToPRD:
    """会议录音转PRD的3阶段流程"""
    
    def __init__(self, input_file: str, output_dir: str = "dialogue_process/output"):
        self.input_file = Path(input_file)
        self.timing_stats = {}  # 添加时间统计
        self.output_dir = Path(output_dir)
        
    def check_dependencies(self):
        """检查必要的依赖 - 强制断言版本"""
        dependencies = {
            'ffmpeg': ['ffmpeg', '-version'],
            'ffprobe': ['ffprobe', '-version']
        }
        
        missing_deps = []
        for name, cmd in dependencies.items():
            try:
                subprocess.run(cmd, capture_output=True, check=True)
                logger.info(f"✅ {name} 已安装")
            except (subprocess.CalledProcessError, FileNotFoundError):
                missing_deps.append(name)
        
        # 🚨 断言：不允许缺少依赖的情况下继续
        if missing_deps:
            error_msg = f"❌ 缺少必要依赖: {', '.join(missing_deps)}"
            logger.error(error_msg)
            logger.error("请安装: brew install ffmpeg (macOS) 或 apt-get install ffmpeg (Ubuntu)")
            raise RuntimeError(error_msg)  # 断言失败，强制中止
        
        return True
    
    def _get_duration_ffprobe(self):
        """使用ffprobe获取时长"""
        result = subprocess.run([
            'ffprobe', '-v', 'error', '-show_entries', 
            'format=duration', '-of', 'default=noprint_wrappers=1:nokey=1',
            str(self.input_file)
        ], capture_output=True, text=True, check=True)
        return float(result.stdout.strip())
    
    def _get_duration_torchaudio(self):
        """使用torchaudio获取时长"""
        import torchaudio
        info = torchaudio.info(str(self.input_file))
        return info.num_frames / info.sample_rate
    
    def _get_duration_librosa(self):
        """使用librosa获取时长"""
        import librosa
        duration = librosa.get_duration(path=str(self.input_file))
        return duration
    
    def _get_duration_estimate(self):
        """基于文件大小估算时长"""
        file_size_mb = self.input_file.stat().st_size / (1024 * 1024)
        # 假设：1MB约为1分钟的音频（保守估算）
        estimated_duration = file_size_mb * 60
        logger.warning(f"使用文件大小估算时长: {estimated_duration:.1f}秒")
        return estimated_duration
    
    def get_audio_duration(self):
        """获取音频时长 - 多重备份方案"""
        methods = [
            ("ffprobe", self._get_duration_ffprobe),
            ("torchaudio", self._get_duration_torchaudio),
            ("librosa", self._get_duration_librosa),
            ("估算", self._get_duration_estimate)
        ]
        
        last_error = None
        for i, (method_name, method) in enumerate(methods):
            try:
                duration = method()
                if duration and duration > 0:
                    logger.info(f"✅ {method_name}获取时长成功: {duration:.2f}秒")
                    return duration
            except Exception as e:
                logger.warning(f"{method_name}方法失败: {e}")
                last_error = e
                continue
        
        # 🚨 断言：所有方法都失败时强制中止
        error_msg = f"❌ 无法通过任何方法获取音频时长，最后错误: {last_error}"
        logger.error(error_msg)
        raise RuntimeError(error_msg)
    
    def check_file_size(self):
        """检查文件大小并给出建议"""
        file_size = self.input_file.stat().st_size
        file_mb = file_size / (1024 * 1024)
        
        # 使用配置的阈值判断大文件（转换为MB）
        large_file_threshold_mb = file_config.large_file_threshold * 160  # 估算：1秒约160KB
        if file_mb > large_file_threshold_mb:
            logger.warning(f"文件较大 ({file_mb:.1f}MB)，将自动使用分割处理")
            return False
        return True
        
    def validate_robust_processor(self):
        """验证Stage1处理器可用性（已移除并行处理器）"""
        try:
            from dialogue_process.stages.stage1_raw import Stage1Processor
            logger.info("✅ Stage1处理器导入成功（单线程模式）")
            return Stage1Processor, None  # 不再需要并行配置
        except ImportError as e:
            error_msg = f"❌ Stage1处理器不可用: {e}"
            logger.error(error_msg)
            logger.error("请确保 dialogue_process.stages.stage1_raw 存在且依赖完整")
            raise RuntimeError(error_msg)
    
    def comprehensive_pre_check(self):
        """综合预检查 - 在处理前验证所有条件"""
        checks = [
            ("依赖检查", self.check_dependencies),
            ("音频文件验证", self._validate_audio_file),
            ("鲁棒处理器", self.validate_robust_processor),
            ("系统资源", self._check_system_resources)
        ]
        
        results = {}
        for check_name, check_func in checks:
            try:
                logger.info(f"🔍 执行{check_name}...")
                result = check_func()
                results[check_name] = result
                logger.info(f"✅ {check_name}通过")
            except Exception as e:
                logger.error(f"❌ {check_name}失败: {e}")
                raise RuntimeError(f"预检查失败: {check_name} - {e}")
        
        return results
    
    def _validate_audio_file(self):
        """验证音频文件"""
        if not self.input_file.exists():
            raise RuntimeError(f"音频文件不存在: {self.input_file}")
        
        if self.input_file.stat().st_size == 0:
            raise RuntimeError("音频文件为空")
        
        # 检查文件格式
        valid_extensions = ['.wav', '.mp3', '.m4a', '.flac', '.aac']
        if self.input_file.suffix.lower() not in valid_extensions:
            raise RuntimeError(f"不支持的音频格式: {self.input_file.suffix}")
        
        return True
    
    def _check_system_resources(self):
        """检查系统资源"""
        import psutil
        
        # 检查内存
        memory = psutil.virtual_memory()
        if memory.available < 2 * 1024 * 1024 * 1024:  # 小于2GB
            logger.warning(f"可用内存较少: {memory.available / (1024**3):.1f}GB")
        
        # 检查磁盘空间
        disk = psutil.disk_usage(str(self.output_dir.parent))
        if disk.free < 1 * 1024 * 1024 * 1024:  # 小于1GB
            raise RuntimeError(f"磁盘空间不足: {disk.free / (1024**3):.1f}GB")
        
        return True

    def process_large_file_simple(self):
        """简化的大文件处理 - 直接处理，不分片"""
        logger.info("🎵 === 简化处理模式：直接处理整个音频文件 ===")
        
        # 直接使用标准处理流程，不进行分片
        return self.process_standard()
    
    
    
    
    
    
    
    def _format_time(self, seconds: float) -> str:
        """格式化时间显示"""
        if seconds < 60:
            return f"{seconds:.1f}秒"
        elif seconds < 3600:
            minutes = int(seconds // 60)
            secs = seconds % 60
            return f"{minutes}分{secs:.1f}秒"
        else:
            hours = int(seconds // 3600)
            minutes = int((seconds % 3600) // 60)
            secs = seconds % 60
            return f"{hours}小时{minutes}分{secs:.1f}秒"
    
    def _print_performance_stats(self, file_size_mb: float):
        """打印性能统计信息"""
        logger.info("=" * 60)
        logger.info("📊 性能统计报告")
        logger.info("=" * 60)
        logger.info(f"📁 文件大小: {file_size_mb:.1f} MB")
        
        # 各阶段耗时
        stage1_time = self.timing_stats.get("stage1", 0)
        stage2_time = self.timing_stats.get("stage2", 0)
        stage3_time = self.timing_stats.get("stage3", 0)
        total_time = self.timing_stats.get("total", 0)
        
        logger.info(f"⏱️  阶段1 (语音转录): {self._format_time(stage1_time)}")
        logger.info(f"⏱️  阶段2 (语义分析): {self._format_time(stage2_time)}")
        logger.info(f"⏱️  阶段3 (报告生成): {self._format_time(stage3_time)}")
        logger.info(f"🕐 总处理时间: {self._format_time(total_time)}")
        
        # 性能指标
        if total_time > 0:
            mb_per_sec = file_size_mb / total_time
            logger.info(f"🚀 处理速度: {mb_per_sec:.2f} MB/秒")
        
        # 各阶段占比
        if total_time > 0:
            stage1_pct = (stage1_time / total_time) * 100
            stage2_pct = (stage2_time / total_time) * 100
            stage3_pct = (stage3_time / total_time) * 100
            
            logger.info("📈 各阶段时间占比:")
            logger.info(f"   阶段1: {stage1_pct:.1f}%")
            logger.info(f"   阶段2: {stage2_pct:.1f}%")
            logger.info(f"   阶段3: {stage3_pct:.1f}%")
        
        logger.info("=" * 60)
    
    def process(self, chunk_size: int = None):
        """完整的3阶段处理流程（自适应大小）"""
        import psutil
        import os
        
        if chunk_size is None:
            chunk_size = file_config.chunk_size
        total_start_time = time.time()
        
        # 详细的进程和系统信息日志
        process = psutil.Process(os.getpid())
        initial_memory = process.memory_info().rss / 1024 / 1024
        logger.info(f"[TRACE] 🚀 MeetingToPRD.process() 开始 - PID: {os.getpid()}")
        logger.info(f"[TRACE] 输入文件: {self.input_file}")
        logger.info(f"[TRACE] 输出目录: {self.output_dir}")
        logger.info(f"[MEMORY] 进程初始内存: {initial_memory:.1f}MB")
        
        # 获取文件信息
        file_size_mb = self.input_file.stat().st_size / (1024 * 1024)
        logger.info(f"[TRACE] 📁 文件大小: {file_size_mb:.1f} MB")
        
        # 系统资源检查
        try:
            cpu_count = os.cpu_count()
            available_memory = psutil.virtual_memory().available / 1024 / 1024 / 1024  # GB
            logger.info(f"[SYSTEM] CPU核心数: {cpu_count}, 可用内存: {available_memory:.1f}GB")
        except Exception as e:
            logger.warning(f"[SYSTEM] 系统资源检查失败: {e}")
        
        # 检查文件大小，但不再分片处理
        if not self.check_file_size():
            logger.info("检测到大文件，使用简化处理模式（不分片）")
            return self.process_large_file_simple()
        
        return self.process_standard()
        
    def process_standard(self):
        """标准的3阶段处理流程"""
        total_start_time = time.time()
        logger.info("📝 === 使用标准3阶段处理架构 ===")
        
        try:
            # 获取文件大小信息
            file_size_mb = self.input_file.stat().st_size / (1024 * 1024)
            
            # 阶段1: 纯语音转录
            logger.info("🎤 === 第1步: 语音转录 [步骤1/3] ===")
            stage1_start = time.time()
            logger.info(f"🚀 [步骤1/3] 开始语音转录...")
            stage1 = Stage1Processor(self.input_file, self.output_dir)
            stage1_result = stage1.process()
            stage1_time = time.time() - stage1_start
            self.timing_stats["stage1"] = stage1_time
            logger.info(f"✅ [步骤1/3] 阶段1转录完成，用时: {self._format_time(stage1_time)}")
            
            # 内存清理
            logger.info("🧹 执行内存清理...")
            gc.collect()
            
            # 阶段2: 智能语义分析
            logger.info("🧠 === 第2步: 智能语义分析 [步骤2/3] ===")
            stage2_start = time.time()
            logger.info("🚀 [步骤2/3] 开始语义分析...")
            stage2 = Stage2SemanticAnalyzer(stage1_result, self.output_dir)
            stage2_result = stage2.process()
            stage2_time = time.time() - stage2_start
            self.timing_stats["stage2"] = stage2_time
            logger.info(f"✅ [步骤2/3] 阶段2语义分析完成，用时: {self._format_time(stage2_time)}")
            
            # 内存清理
            logger.info("🧹 执行内存清理...")
            gc.collect()
            
            # 阶段3: 报告生成
            logger.info("📄 === 第3步: 报告生成 [步骤3/3] ===")
            stage3_start = time.time()
            logger.info("🚀 [步骤3/3] 开始生成PRD文档...")
            stage3 = Stage3ReportGenerator(stage2_result, self.output_dir)
            final_result = stage3.process()
            stage3_time = time.time() - stage3_start
            self.timing_stats["stage3"] = stage3_time
            logger.info(f"✅ [步骤3/3] 阶段3报告生成完成，用时: {self._format_time(stage3_time)}")
            
            # 计算总时间
            total_time = time.time() - total_start_time
            self.timing_stats["total"] = total_time
            
            # 输出性能统计
            logger.info("📊 === 处理完成统计 ===")
            self._print_performance_stats(file_size_mb)
            
            # 输出结果
            generated_files = final_result["data"]["generated_files"]
            logger.info("🎉 [COMPLETE] 处理成功完成！")
            logger.info(f"📈 [COMPLETE] 总处理时间: {self._format_time(total_time)}")
            logger.info("=" * 50)
            logger.info("📁 生成的文件:")
            for file_type, file_path in generated_files.items():
                logger.info(f"  📄 {file_type}: {file_path}")
            logger.info("=" * 50)
            
            return {
                "stage1_result": stage1_result,
                "final_result": final_result,
                "timing_stats": self.timing_stats
            }
            
        except Exception as e:
            logger.error(f"处理失败: {e}")
            import traceback
            traceback.print_exc()
            raise

def main():
    parser = argparse.ArgumentParser(description="会议录音转PRD工具 (重构版)")
    parser.add_argument("input", help="输入音频文件路径")
    parser.add_argument("--output", default="dialogue_process/output", help="输出目录")
    parser.add_argument("--chunk-size", type=int, default=None, help="分段大小（秒），默认使用配置文件中的值")
    
    args = parser.parse_args()
    
    if not os.path.exists(args.input):
        logger.error(f"输入文件不存在: {args.input}")
        sys.exit(1)
    
    processor = MeetingToPRD(args.input)
    try:
        # 如果命令行没有指定chunk_size，使用配置文件的默认值
        chunk_size = args.chunk_size if args.chunk_size is not None else file_config.chunk_size
        result = processor.process(chunk_size)
        if result:
            print("\n✅ 成功完成会议录音分析，请查看生成的文件！")
        else:
            print("\n❌ 处理失败")
            sys.exit(1)
    except Exception as e:
        logger.error(f"处理失败: {e}")
        sys.exit(1)

if __name__ == "__main__":
    main()